Skip to content

feat: Add UI button to copy direct message links#13103

Open
SoleroTG wants to merge 1 commit into
nextcloud:mainfrom
SoleroTG:feat/copy-message-link-ui
Open

feat: Add UI button to copy direct message links#13103
SoleroTG wants to merge 1 commit into
nextcloud:mainfrom
SoleroTG:feat/copy-message-link-ui

Conversation

@SoleroTG

Copy link
Copy Markdown
Contributor

This PR provides the frontend UI implementation for message-id deep links, completing the foundational work established in #12632 and the CSRF fix in #12959.

It adds a new action button to the MenuEnvelope component (the "More actions" menu on a message) to easily copy the generated link.

Key implementations:

  • Wording: Implements the exact phrasing suggested by @ChristophWurst in Deep links for message IDs #2269: "Copy direct link for other user. This only works if they have received the same email."
  • Clipboard API: Uses navigator.clipboard.writeText() for a seamless user experience in secure contexts.
  • Visual Feedback: Added visual confirmation. Upon successful copy, a toast notification (showSuccess) is triggered, and the menu icon temporarily changes to a checkmark before the menu closes.
  • Standardized Fallback: If the modern Clipboard API is unavailable (e.g., local development over HTTP without secure context) or permission is denied, it safely falls back to window.prompt. This directly mirrors the fallback behavior established in @nextcloud/vue's useCopy composable.

Resolves #2269

To Test:

  1. Open the Mail app.
  2. Click the three dots (...) on any message to open the actions menu.
  3. Click "Copy direct link for other user..."
  4. Verify the link is copied to your clipboard and test opening it in a new tab.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The MenuEnvelope component gains a "Copy direct link" entry in its "More actions" submenu. A new ContentCopyIcon is imported and registered. A reactive copied data field (initialized to false) drives conditional icon and label rendering on the new ActionButton. The onCopyMessageLink method trims the envelope messageId, constructs a deep-link URL, attempts to write it to the clipboard via navigator.clipboard.writeText, sets copied to true on success, or falls back to window.prompt on failure. After 2 seconds, copied is reset and localMoreActionsOpen is set to false.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a UI button to copy direct message links in the MenuEnvelope component.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, providing context, implementation details, and testing instructions for the new copy link feature.
Linked Issues check ✅ Passed The PR implements the core requirement from #2269 by adding UI functionality to copy permanent deep links to messages using message IDs, as requested in the feature.
Out of Scope Changes check ✅ Passed All changes are focused on the MenuEnvelope component and directly support the objective of implementing a copy link button feature; no out-of-scope modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/MenuEnvelope.vue (1)

1-4: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Update SPDX header to the required repository format

This .vue file header does not match the mandated SPDX header template for touched JS/Vue files.

As per coding guidelines, "**/*.{php,js,ts,tsx,vue}: Every file must include an SPDX license header. ... Header format: /* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */".

Source: Coding guidelines

🧹 Nitpick comments (1)
src/components/MenuEnvelope.vue (1)

667-670: ⚡ Quick win

De-duplicate/reset the copy-state timeout

Repeated clicks enqueue multiple timers, which can cause stale state flips and inconsistent menu-close timing.

Proposed refactor
 data() {
 	return {
+		copyResetTimer: null,
 		copied: false,
 	}
 },
 ...
 finally {
+	if (this.copyResetTimer) {
+		clearTimeout(this.copyResetTimer)
+	}
-	setTimeout(() => {
+	this.copyResetTimer = setTimeout(() => {
 		this.copied = false
 		this.localMoreActionsOpen = false
+		this.copyResetTimer = null
 	}, 2000)
 }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 779cf0e3-cfcd-478f-938a-ca84d05cee6e

📥 Commits

Reviewing files that changed from the base of the PR and between 5b435b9 and ed6ac73.

📒 Files selected for processing (1)
  • src/components/MenuEnvelope.vue

Comment on lines +655 to +656
const trimmedMessageId = this.envelope.messageId.trim().replace(/^<|>$/g, '')
const url = window.location.origin + generateUrl('/apps/mail/open/' + encodeURIComponent(trimmedMessageId))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard messageId before calling trim()

Line 655 can throw when this.envelope.messageId is missing/null, which breaks the action instead of falling back gracefully.

Proposed fix
 async onCopyMessageLink() {
-	const trimmedMessageId = this.envelope.messageId.trim().replace(/^<|>$/g, '')
+	const rawMessageId = this.envelope?.messageId
+	if (typeof rawMessageId !== 'string' || rawMessageId.trim() === '') {
+		showError(t('mail', 'Could not generate direct link for this message'))
+		return
+	}
+	const trimmedMessageId = rawMessageId.trim().replace(/^<|>$/g, '')
 	const url = window.location.origin + generateUrl('/apps/mail/open/' + encodeURIComponent(trimmedMessageId))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const trimmedMessageId = this.envelope.messageId.trim().replace(/^<|>$/g, '')
const url = window.location.origin + generateUrl('/apps/mail/open/' + encodeURIComponent(trimmedMessageId))
async onCopyMessageLink() {
const rawMessageId = this.envelope?.messageId
if (typeof rawMessageId !== 'string' || rawMessageId.trim() === '') {
showError(t('mail', 'Could not generate direct link for this message'))
return
}
const trimmedMessageId = rawMessageId.trim().replace(/^<|>$/g, '')
const url = window.location.origin + generateUrl('/apps/mail/open/' + encodeURIComponent(trimmedMessageId))

Implements a UI button in the MenuEnvelope to generate and copy a direct link to the message using the new deep link route. Utilizes the standard Nextcloud clipboard fallback via window.prompt for non-secure contexts.

Signed-off-by: SoleroTG <github-29h@solero.quietmail.eu>
@SoleroTG SoleroTG force-pushed the feat/copy-message-link-ui branch from ed6ac73 to 62b0de7 Compare June 18, 2026 05:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deep links for message IDs

1 participant